package com.colin.jipay.unionpay;

import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;

import com.colin.jipay.utils.ACache;
import com.colin.jipay.utils.JsonUtils;
import com.colin.jipay.utils.LogUtil;
import com.colin.jipay.utils.PayHelperUtils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class UnionHook {
    private static ClassLoader mClassLoader;
    private static Context mContext;
    public static String QRCODERECEIVED_ACTION = "com.tools.payhelper.qrcodereceived";
    public static String BILLRECEIVED_ACTION = "com.tools.payhelper.billreceived";
    public static Class UPPushService = null;

    private static String last_orderTime;
    private static boolean is_first_check = true;
    private static int forTime = 0;
    private static int doTime = 0;

    private static ACache aCache;
    private static List<String> moneys = new ArrayList<>();

    public void hook(final ClassLoader classLoader, final Context context) {
        mClassLoader = classLoader;
        mContext = context;

        aCache = ACache.get(mContext);
        hookGetOrder(mClassLoader);
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                CheckNewOrder();
                callMethodGetOrder(mClassLoader);
//                CheckNewOrder();
//                PayHelperUtils.sendmsg(context,"tn= "+last_orderTime);
            }
        };

        timer.scheduleAtFixedRate(task, 10000, 2000);
        mlog("开启轮询");


        Timer timer1 = new Timer();
        timer1.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                moneys.clear();
                aCache.remove("money");
            }
        }, 100, 1000 * 60 * 5);

    }

    public static void hookPushService(final Context context) {
        XposedHelpers.findAndHookMethod(UPPushService, "c", String.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(final MethodHookParam param) throws Throwable {
                String arg2 = (String) param.args[0];
                mlog(arg2);
                try {
                    if (arg2.contains("付款") && arg2.contains("动账通知")) {
                        JSONObject o = new JSONObject(arg2);
                        String alert = o.getJSONObject("body").getString("alert");
                        mlog("FORRECODE alert:" + alert);
                        String pre = alert.split("元,")[0];
                        String parts[] = pre.split("通过扫码向您付款");
                        // Toast.makeText(activity, pre, Toast.LENGTH_SHORT).show();
                        if (parts.length == 2) {
                            final String u = parts[0];
                            final String m = parts[1];
                            mlog("New Push Msg u:" + u + " m:" + m);

                            new Handler().postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    Intent intent = new Intent("com.colin.union.notify");
                                    intent.putExtra("name", u);
                                    intent.putExtra("title", m);
                                    context.sendBroadcast(intent);
                                    PayHelperUtils.sendmsg(context, "通知发送回调");
                                }
                            }, 2000);

                        }
                    }
                } catch (Throwable e) {
                    mlog(e);
                }
            }
        });
    }

    public static void doGetQrCode(final String money, final String mark) {
        getVirtualCardNum(new GetCardNumListener() {
            @Override
            public void success(String re) {
                time = System.currentTimeMillis();
                GenQrCode(money, mark);
            }

            @Override
            public void error(String error) {

            }
        });
    }

    public static void GenQrCode(final String money, final String mark) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String mark1 = mark;
                    String money1 = new BigDecimal(money).setScale(2, RoundingMode.HALF_UP).toPlainString().replace(".", "");
                    XposedBridge.log("FORRECODE GenQrCode:0 money:" + money1 + " mark:" + mark1);
                    String str2 = "https://pay.95516.com/pay-web/restlet/qr/p2pPay/applyQrCode?txnAmt=" + Enc(money1) + "&cityCode=" + Enc(getcityCd()) + "&comments=" + Enc(mark1) + "&virtualCardNo=" + encvirtualCardNo;
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder()
                            .url(str2)
                            .header("X-Tingyun-Id", getXTid())
                            .header("X-Tingyun-Lib-Type-N-ST", "0;" + System.currentTimeMillis())
                            .header("sid", getSid())
                            .header("urid", geturid())
                            .header("cityCd", getcityCd())
                            .header("locale", "zh-CN")
                            .header("User-Agent", "Android CHSP")
                            .header("dfpSessionId", getDfpSessionId())
                            .header("gray", getgray())
                            .header("key_session_id", "")
                            .header("Host", "pay.95516.com")
                            .build();

                    Response response = client.newCall(request).execute();
                    String RSP = response.body().string();
                    mlog("GenQrCode str2=>" + str2 + " RSP=>" + RSP);
                    String Rsp = Dec(RSP);
                    mlog(" RSP=>" + Rsp);

                    try {
                        JSONObject o = new JSONObject(Rsp);
                        o.put("mark", mark);
                        o.put("money", money);
                        JSONObject payurljson = o.getJSONObject("params");
                        String payurl = "";
                        if (payurljson.has("certificate")) {
                            payurl = payurljson.get("certificate").toString();
                        }

                        Intent broadCastIntent = new Intent();
                        broadCastIntent.putExtra("money", money);
                        broadCastIntent.putExtra("mark", mark);
                        broadCastIntent.putExtra("type", "unionpay");
                        broadCastIntent.putExtra("payurl", payurl);
                        broadCastIntent.setAction(QRCODERECEIVED_ACTION);
                        mContext.sendBroadcast(broadCastIntent);
                    } catch (Throwable e) {
                        mlog(e);
                    }

                } catch (Throwable e) {
                    mlog(e);

                }

            }

        }).start();
    }

    interface GetCardNumListener {
        void success(String s);

        void error(String s);
    }

    public static String encvirtualCardNo;
    public static Long time;

    public static void getVirtualCardNum(final GetCardNumListener listener) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    mlog("GetVirtualCardNum");
                    String str2 = "https://pay.95516.com/pay-web/restlet/qr/p2pPay/getInitInfo?cardNo=&cityCode=" + Enc(getcityCd());
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder()
                            .url(str2)
                            .header("X-Tingyun-Id", getXTid())
                            .header("X-Tingyun-Lib-Type-N-ST", "0;" + System.currentTimeMillis())
                            .header("sid", getSid())
                            .header("urid", geturid())
                            .header("cityCd", getcityCd())
                            .header("locale", "zh-CN")
                            .header("User-Agent", "Android CHSP")
                            .header("dfpSessionId", getDfpSessionId())
                            .header("gray", getgray())
                            .header("key_session_id", "")
                            .header("Host", "pay.95516.com")
                            .build();
                    Response response = client.newCall(request).execute();
                    String RSP = response.body().string();
                    mlog("GetVirtualCardNum str2=>" + str2 + " RSP=>" + RSP);
                    String Rsp = Dec(RSP);
                    mlog("GetVirtualCardNum str2=>" + str2 + " RSP=>" + Rsp);
                    try {
                        encvirtualCardNo = Enc(new JSONObject(Rsp).getJSONObject("params").getJSONArray("cardList").getJSONObject(0).getString("virtualCardNo"));
                        mlog("encvirtualCardNo" + encvirtualCardNo);
                        if (listener != null) {
                            listener.success(encvirtualCardNo);
                        }
                    } catch (Throwable e) {
                        mlog(e);
                        if (listener != null) {
                            listener.error(e.getMessage() + e.getCause());
                        }
                    }


                } catch (Throwable e) {
                    mlog(e);
                    if (listener != null) {
                        listener.error(e.getMessage() + e.getCause());
                    }
                }
            }
        }).start();

    }

    public static String Dec(String src) {
        try {
            return (String) XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.unionpay.encrypt.IJniInterface", mClassLoader), "decryptMsg", src);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String Enc(String src) {
        try {
            return (String) XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.unionpay.encrypt.IJniInterface", mClassLoader), "encryptMsg", src);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return "";
    }

    private static String getXTid() {
        try {
            Class m_s = XposedHelpers.findClass("com.networkbench.agent.impl.m.s", mClassLoader);
            Object f = XposedHelpers.callStaticMethod(m_s, "f");
            Object h = XposedHelpers.callMethod(f, "H");
            mlog("h=>" + h);
            Object i = XposedHelpers.callStaticMethod(m_s, "I");

            String xtid = m_s.getDeclaredMethod("a", String.class, int.class).invoke(null, h, i).toString();
            mlog("xtid:" + xtid + "");
            return xtid;
        } catch (Throwable e) {
            mlog(e);
        }
        return null;
    }

    private static String getSid() {
        String sid = "";
        try {
            Object b = XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.unionpay.network.aa", mClassLoader), "b");
            sid = XposedHelpers.callMethod(b, "e").toString();
        } catch (Throwable e) {
            mlog(e);
        }
        mlog("sid:" + sid + "");
        return sid;
    }

    private static String geturid() {
        String Cacheurid = "";
        try {
            Class data_d = XposedHelpers.findClass("com.unionpay.data.d", mClassLoader);
            Object o = XposedHelpers.callStaticMethod(data_d, "a", new Class[]{Context.class}, new Object[]{mContext});
            String v1_2 = XposedHelpers.callMethod(XposedHelpers.callMethod(o, "A"), "getHashUserId").toString();
            if (!TextUtils.isEmpty(v1_2) && v1_2.length() >= 15) {
                Cacheurid = v1_2.substring(v1_2.length() - 15);
            }
        } catch (Throwable e) {
            mlog(e);
        }
        mlog("Cacheurid:" + Cacheurid + "");
        return Cacheurid;
    }

    private static String getDfpSessionId() {
        String CacheDfpSessionId = "";
        try {
            Object o = XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.unionpay.service.b", mClassLoader), "d");
            mlog("o=>" + o);
            CacheDfpSessionId = o.toString();
        } catch (Throwable e) {
            mlog(e);
        }
        mlog("CacheDfpSessionId:" + CacheDfpSessionId + "");
        return CacheDfpSessionId;
    }

    private static String getcityCd() {
        String CachecityCd = "";
        try {
            CachecityCd = XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.unionpay.location.a", mClassLoader), "i").toString();
        } catch (Throwable e) {
            mlog(e);
        }
        mlog("CachecityCd:" + CachecityCd + "");
        return CachecityCd;
    }

    private static String getgray() {
        String Cachegray = "";
        try {
            Object b = XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.unionpay.network.aa", mClassLoader), "b");
            Cachegray = XposedHelpers.callMethod(b, "d").toString();
        } catch (Throwable e) {
            mlog(e);
        }
        mlog("Cachegray:" + Cachegray + "");
        return Cachegray;
    }

    /**
     * hook订单信息
     */
    private void hookGetOrder(ClassLoader classLoader) {


        //hook订单信息
        XposedHelpers.findAndHookMethod("com.unionpay.network.u", classLoader, "onResult",
                XposedHelpers.findClassIfExists("com.unionpay.network.model.UPID", classLoader),
                String.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        String UPID = param.args[0].toString();
                        if (UPID.contains("131") && UPID.contains("1001")) {
                            String result = param.args[1].toString();
                            try {
                                JSONObject jsonObject0 = new JSONObject(result);
                                JSONObject jsonObject1 = jsonObject0.getJSONObject("params");

                                org.json.JSONArray jsonArray1 = new org.json.JSONArray(jsonObject1.optString("transRecordsList"));
                                for (int i = 1; i <= jsonArray1.length(); i++) {
                                    org.json.JSONObject jsonObject2 = jsonArray1.getJSONObject((jsonArray1.length() - i));
                                    if (jsonObject2.optString("inOutType").equals("02")) {
                                        String orderCode = jsonObject2.optString("key");
                                        String money2 = String.format(Locale.CANADA, "%.2f", Double.parseDouble(jsonObject2.optString("transAmount")) / 100);
                                        String mchntClassNm = jsonObject2.optString("mchntClassNm");
                                        String transTime = Calendar.getInstance().get(Calendar.YEAR) + orderCode.substring(41, 51);
                                        XposedBridge.log("订单号：" + orderCode + "\n金额" + money2 + "\n时间" + transTime);
                                        if (aCache.getAsObject("time") == null && !cacheMoneyList().contains(money2)) {
                                            aCache.put("time", Double.parseDouble(transTime));
                                            if (cacheMoneyList().contains(money2) && cacheMoneyList().equals("[]")) {
                                                return;
                                            }
                                            Log.e("MainXposed1", orderCode + "," + money2 + "," + transTime);
                                            moneys.add(money2);
                                            aCache.put("money", (Serializable) moneys);
                                            Intent intent = new Intent("com.tools.unionpay.billreceived");
                                            intent.putExtra("bill_no", orderCode);
                                            intent.putExtra("money", money2);
                                            intent.putExtra("bill_mark", mchntClassNm);
                                            intent.putExtra("bill_type", "unionpay");
                                            mContext.sendBroadcast(intent);
                                        } else {
                                            double cacheTime = (double) aCache.getAsObject("time");
                                            double currentTime = Double.parseDouble(transTime);
                                            if (currentTime > cacheTime && !cacheMoneyList().contains(money2)) {
                                                aCache.put("time", currentTime);
                                                if (cacheMoneyList().contains(money2) && cacheMoneyList().equals("[]")) {
                                                    return;
                                                }
                                                Log.e("MainXposed2", orderCode + "," + money2 + "," + transTime + ",,," + cacheMoneyList());
                                                moneys.add(money2);
                                                aCache.put("money", (Serializable) moneys);

                                                Intent intent = new Intent("com.tools.unionpay.billreceived");
                                                intent.putExtra("bill_no", orderCode);
                                                intent.putExtra("money", money2);
                                                intent.putExtra("bill_mark", mchntClassNm);
                                                intent.putExtra("bill_type", "unionpay");
                                                mContext.sendBroadcast(intent);
                                            }
                                        }
                                    }
                                }

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
    }

    /**
     * 主动拉起订单方法
     */
    private void callMethodGetOrder(ClassLoader classLoader) {

        try {
            Class<?> aaClass = XposedHelpers.findClassIfExists("com.unionpay.network.aa", classLoader);
            Object aaObject = XposedHelpers.callStaticMethod(aaClass, "b", new Class[]{Context.class}, mContext);

            Class<?> eClass = XposedHelpers.findClassIfExists("com.unionpay.network.e", classLoader);
            Object eObject = Proxy.newProxyInstance(classLoader, new Class[]{eClass}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return null;
                }
            });

            Class<?> uClass = XposedHelpers.findClassIfExists("com.unionpay.network.u", classLoader);
            Class<?> lClass = XposedHelpers.findClassIfExists("com.android.volley.l", classLoader);
            Object uObject = XposedHelpers.newInstance(uClass, new Class[]{lClass, eClass}, aaObject, eObject);

            Class<?> UPIDClass = XposedHelpers.findClassIfExists("com.unionpay.network.model.UPID", classLoader);
            Object UPIDObject = XposedHelpers.newInstance(UPIDClass, new Class[]{int.class, Object.class}, 131, 1001);
            String url = "https://wallet.95516.com/acc/inApp/bigData/records";
            Class<?> encryptClass = XposedHelpers.findClassIfExists("com.unionpay.network.EncryptValue$Encrypt", classLoader);
            Object encryptObject = encryptClass.getDeclaredField("VID").get("VID");
            Calendar cd = Calendar.getInstance();
            String list = "{\"listCards\":[\"6a032a7c53ad93836211fa22671690b57b4026e8e849b79d\"],\"month\":\"" + (cd.get(Calendar.MONDAY) + 1) + "\",\"year\":\"" + cd.get(Calendar.YEAR) + "\"}";

            XposedHelpers.callMethod(uObject, "a", new Class[]{UPIDClass, String.class, encryptClass, String.class}, UPIDObject, url, encryptObject, list);

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    public static void CheckNewOrder() {
        try {
            String str2 = "https://wallet.95516.com/app/inApp/order/list?currentPage=" + Enc("1") + "&month=" + Enc("0") + "&orderStatus=" + Enc("0") + "&orderType=" + Enc("A30000") + "&pageSize=" + Enc("10") + "";
            OkHttpClient client = null;
            OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
            builder.connectTimeout(50, TimeUnit.SECONDS);
            builder.writeTimeout(50, TimeUnit.SECONDS);
            builder.readTimeout(50, TimeUnit.SECONDS);
            client = builder.build();
            Request request = new Request.Builder()
                    .url(str2)
                    .header("X-Tingyun-Id", getXTid())
                    .header("X-Tingyun-Lib-Type-N-ST", "0;" + System.currentTimeMillis())
                    .header("sid", getSid())
                    .header("urid", geturid())
                    .header("cityCd", getcityCd())
                    .header("locale", "zh-CN")
                    .header("User-Agent", "Android CHSP")
                    .header("dfpSessionId", getDfpSessionId())
                    .header("gray", getgray())
                    .header("Accept", "*/*")
                    .header("key_session_id", "")
                    .header("Host", "wallet.95516.com")
                    .build();
            Response response = client.newCall(request).execute();
            String RSP = response.body().string();
            LogUtil.log("CheckNewOrder str2=>" + str2 + " RSP=>" + RSP);
            String DecRsp = Dec(RSP);
            mlog("CheckNewOrder str2=>" + str2 + " DecRSP=>" + DecRsp);
            //这里有很多笔，可以自己调整同步逻辑s  （可以考虑用数据库缓存，失败的时候再自己开线程去查询补单）
            JSONArray o = new JSONObject(DecRsp).getJSONObject("params").getJSONArray("uporders");
            if (o.length() == 0) {
                return;
            }
            for (int i = 0; i < o.length(); i++) {
                JSONObject p = o.getJSONObject(i);
                String money = p.getString("amount");
                String orderid = p.getString("orderId");
                String orderTime = p.getString("tn");
                if (is_first_check) {
                    last_orderTime = orderTime;
                    is_first_check = false;
                    return;
                }
//                XposedBridge.log("1:"+last_orderTime);
//                XposedBridge.log("2:"+orderTime);
                if (!last_orderTime.equals(orderTime)) {
                    forTime++;
                    XposedBridge.log("p-" + forTime + ":" + p.toString());
                    XposedBridge.log("3:" + orderTime);
                    DoOrderInfoGet(orderid, money);
                } else {
                    if (i > 0) {
                        last_orderTime = o.getJSONObject(i - 1).getString("tn");
                    }
                    return;
                }
//                if (p.getString("amount").equals(money) && p.getString("title").contains(user)) {
//                    return DoOrderInfoGet(orderid,money);
//                }

            }
        } catch (Throwable e) {
            mlog(e);
        }
    }

    public static String DoOrderInfoGet(String orderid, String money) {
        if (orderid.length() > 5) {
            try {
                int tryAgain = 0;
                String args = "{\"orderType\":\"21\",\"transTp\":\"simple\",\"orderId\":\"" + orderid + "\"}";
                String str2 = "https://wallet.95516.com/app/inApp/order/detail";
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        .url(str2)
                        .header("X-Tingyun-Id", getXTid())
                        .header("X-Tingyun-Lib-Type-N-ST", "0;" + System.currentTimeMillis())
                        .header("sid", getSid())
                        .header("urid", geturid())
                        .header("cityCd", getcityCd())
                        .header("locale", "zh-CN")
                        .header("User-Agent", "Android CHSP")
                        .header("dfpSessionId", getDfpSessionId())
                        .header("gray", getgray())
                        .header("Accept", "*/*")
                        .header("key_session_id", "")
                        .header("Content-Type", "application/json; charset=utf-8")
                        .header("Host", "wallet.95516.com")
                        .post(RequestBody.create(null, Enc(args)))
                        .build();
                Response response = client.newCall(request).execute();
                String RSP = response.body().string();
                LogUtil.log("DoOrderInfoGet str2=>" + str2 + " RSP=>" + RSP);
                String DecRsp = Dec(RSP);
                LogUtil.log("FORRECODE DoOrderInfoGet str2=>" + str2 + " DecRSP=>" + DecRsp);
                if (!new JSONObject(DecRsp).getString("resp").equals("00")) {
                    tryAgain++;
                    if (tryAgain > 10) {
                        PayHelperUtils.sendmsg(mContext, "云闪付订单：" + orderid + "-金额：" + money + "获取详情超过10次未成功，请主动检查订单！");
                        return "";
                    }
                    PayHelperUtils.sendmsg(mContext, "云闪付订单：" + orderid + "-金额：" + money + "详情获取失败，2s后进行第" + tryAgain + "次重试！");
                    Thread.sleep(2000);
                    DoOrderInfoGet(orderid, money);
                    return "";
                }
                //这里有很多笔，可以自己调整同步逻辑s
                JSONObject params = new JSONObject(DecRsp).getJSONObject("params");
                String orderDetail = params.getString("orderDetail");
                LogUtil.log("FORRECODE DoOrderInfoGet str2=>" + str2 + " orderDetail=>" + orderDetail);
                JSONObject o = new JSONObject(orderDetail);
                String u = o.getString("payUserName");
                String mark = o.getString("postScript");
                String totalAmount = params.getString("totalAmount");
                Log.e("MainXposed", "---------" + money);
                if (cacheMoneyList().contains(money)) {
                    return "";
                }
                moneys.add(money);
                aCache.put("money", (Serializable) moneys);

                LogUtil.log("FORRECODE DoOrderInfoGet str2=>" + str2 + " u:" + u + " mark:" + mark + " totalAmount:" + totalAmount);
                Intent broadCastIntent = new Intent(BILLRECEIVED_ACTION);
                broadCastIntent.putExtra("bill_no", o.getString("walletOrderId"));
                broadCastIntent.putExtra("money", money);
                broadCastIntent.putExtra("bill_mark", mark);
                broadCastIntent.putExtra("bill_type", "unionpay");
                mContext.sendBroadcast(broadCastIntent);
                doTime++;
                LogUtil.log("doTime-" + doTime);
                //PayHelperUtils.sendmsg(mContext,"发送回调广播");
                return params.toString();
            } catch (Throwable e) {
                mlog(e);
                return "ERR:" + e.getLocalizedMessage();
            }
        } else {
            return "ERROR_ORDER:" + orderid;
        }
    }

    public static void mlog(String s) {
//        XposedBridge.log(s);
    }

    public static void mlog(Throwable s) {
        mlog(s.getMessage() + "--" + s.getCause());
    }


    private static String cacheMoneyList() {
        List<String> moneys = new ArrayList<>();
        if (aCache.getAsObject("money") != null) {
            moneys.addAll((List<String>) aCache.getAsObject("money"));
        }
        return JsonUtils.objectToString(moneys);
    }
}
